﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GearUp.Crawler.Entities;
using HtmlAgilityPack;

namespace GearUp.Crawler
{
	public class LorebookItemParser : ILorebookItemParser
	{
		public LorebookItem ParseHtmlNode(HtmlAgilityPack.HtmlNode lorebookEntry, string sourceUrl)
		{
			if (lorebookEntry == null)
				return null;

			var itemTableElements = lorebookEntry.Descendants("table").Count(d => d.CssClassIs("item"));
			if (itemTableElements == 0)
				return null; //This is not an item page

			var item = new LorebookItem(sourceUrl);
			item.ItemIconUrl = ExtractAttributeFromHtmlClass(lorebookEntry, "itemicon", "src");
			item.Name = ExtractInnerTextFromDiv(lorebookEntry, "itemname");
			item.ItemBinding = ExtractItemBindingType(lorebookEntry);
			item.UniqueItem = ExtractInnerTextFromDiv(lorebookEntry, "itemunique").Equals("Unique");
			item.Effects = ExtractItemEffects(lorebookEntry);
			item.Durability = ExtractValueFromDivInnerText(lorebookEntry, "itemdurability", 2, "Durability", 4);
			item.Sturdiness = ExtractInnerTextFromDiv(lorebookEntry, "itemsturdiness");
			item.MinimumLevel = ExtractValueFromDivInnerText(lorebookEntry, "itemrequirement", 3, "Minimum", 3);
			ExtractClasses(lorebookEntry, item);
			item.Description = ExtractInnerTextFromDiv(lorebookEntry, "itemdescription");
			item.SetName = ExtractSetName(lorebookEntry);
			ExtractSetPieces(lorebookEntry, item);
			ExtractSetEffects(lorebookEntry, item);
			ExtractItemCategory(item);
			item.ArmourClass = ExtractArmourClass(lorebookEntry);
			item.ArmourGain = ExtractArmourGain(lorebookEntry);
			return item;
		}

		private void ExtractSetEffects(HtmlNode lorebookEntry, LorebookItem item)
		{
			var setDiv = lorebookEntry.Descendants("div").FirstOrDefault(d => d.CssClassIs("itemseteff"));
			if (setDiv == null || string.IsNullOrEmpty(setDiv.InnerText))
				return;
			var textElements = setDiv.SelectSingleNode("p").ChildNodes.Where(n => n.NodeType == HtmlNodeType.Text).ToList();
			textElements.ForEach(n =>
			{
				if (!string.IsNullOrEmpty(n.InnerText) && !string.IsNullOrEmpty(n.InnerText.Trim()))
					item.SetEffects.Add(n.InnerText);
			});
			return;
		}

		private void ExtractSetPieces(HtmlNode lorebookEntry, LorebookItem item)
		{
			var setDiv = lorebookEntry.Descendants("div").Where(d => d.CssClassIs("itemsp"));
			if (setDiv.Count() == 0) return;
			setDiv.ToList().ForEach(div =>
			{
				var text = div.FirstChild.InnerText;
				if (!string.IsNullOrEmpty(text))
					item.SetPieces.Add(text);
			});
		}

		private string ExtractSetName(HtmlNode lorebookEntry)
		{
			var setDiv = lorebookEntry.Descendants("div").FirstOrDefault(d => d.CssClassIs("itemsn"));
			try
			{
				var name = setDiv.FirstChild.InnerText; //name is contained in an anchor tag
				return name;
			}
			catch
			{
				return string.Empty;
			}
		}

		private void ExtractClasses(HtmlNode lorebookEntry, LorebookItem item)
		{
			var requirments = lorebookEntry.Descendants("div").FirstOrDefault(d => d.CssClassIs("itemrequirement") && d.InnerText.StartsWith("Class"));
			if (requirments == null || requirments.InnerText == "")
				return;
			requirments.Descendants("span").ToList().ForEach(span =>
			{
				if (!string.IsNullOrEmpty(span.InnerText))
					item.Classes.Add(span.InnerText);
			});
		}

		private int ExtractValueFromDivInnerText(HtmlNode lorebookEntry, string className, int elementWordNumber, string checkFirstWordValue, int expectedWords)
		{
			var innerText = ExtractInnerTextFromDiv(lorebookEntry, className);
			var elements = innerText.Replace("&nbsp;", " ").Split(' ');
			int value = 0;
			if (elements.Count() == expectedWords && elements[0].Equals(checkFirstWordValue))
			{
				int.TryParse(elements[elementWordNumber - 1], out value);
			}
			return value;
		}

		private ItemEffects ExtractItemEffects(HtmlNode lorebookEntry)
		{
			var effects = new ItemEffects();
			var itemEffects = lorebookEntry.Descendants("div").Where(d => d.GetAttributeValue("class", "").Equals("iteme")).Select(d => d.InnerText ?? "");
			effects.PowerBonus = GetItemAttributeValue(itemEffects, "Maximum Power");
			effects.MoraleBonus = GetItemAttributeValue(itemEffects, "Maximum Morale");
			effects.WillBonus = GetItemAttributeValue(itemEffects, "Will");
			effects.MightBonus = GetItemAttributeValue(itemEffects, "Might");
			effects.AgilityBonus = GetItemAttributeValue(itemEffects, "Agility");
			effects.VitalityBonus = GetItemAttributeValue(itemEffects, "Vitality");
			effects.Radiance = GetItemAttributeValue(itemEffects, "Radiance");
			return effects;
		}

		private static int GetItemAttributeValue(IEnumerable<string> itemAttributes, string attributeName)
		{
			int returnValue = 0;
			var elements = itemAttributes.FirstOrDefault(s => s.Contains(attributeName));
			if (string.IsNullOrEmpty(elements))
				return returnValue;
			int.TryParse(elements.Split(' ').First(), out returnValue);
			return returnValue;
		}

		private string ExtractAttributeFromHtmlClass(HtmlNode lorebookEntry, string className, string attribute)
		{
			var x = lorebookEntry.Descendants().FirstOrDefault(i => i.GetAttributeValue("class", "") == className);
			return x == null ? string.Empty : x.GetAttributeValue(attribute, "");
		}

		private string ExtractInnerTextFromDiv(HtmlNode lorebookEntry, string className)
		{
			var x = lorebookEntry.Descendants("div").FirstOrDefault(d => d.CssClassIs(className));
			return x == null ? string.Empty : x.InnerText;
		}

		private ItemBinding ExtractItemBindingType(HtmlNode lorebookEntry)
		{
			var x = ExtractInnerTextFromDiv(lorebookEntry, "itembind");
			if (x.Contains("Acquire"))
				return ItemBinding.BindOnAcquire;
			if (x.Contains("Equip"))
				return ItemBinding.BindOnEquip;
			return ItemBinding.None;
		}

		private ArmourClass ExtractArmourClass(HtmlNode lorebookEntry)
		{
			var x = ExtractInnerTextFromDiv(lorebookEntry, "itemtype");
			if (x.StartsWith("Light"))
				return ArmourClass.Light;
			if (x.StartsWith("Medium"))
				return ArmourClass.Medium;
			if (x.StartsWith("Heavy"))
				return ArmourClass.Heavy;
			return ArmourClass.None;
		}

		private void ExtractItemCategory(LorebookItem item)
		{
			item.ItemCategory = ItemCategory.None;
			if (item.ItemIconUrl.Contains(@"/shoulders/"))
				item.ItemCategory = ItemCategory.Shoulders;
			if (item.ItemIconUrl.Contains(@"/chest/"))
				item.ItemCategory = ItemCategory.Chest;
			if (item.ItemIconUrl.Contains(@"/head/"))
				item.ItemCategory = ItemCategory.Head;
			if (item.ItemIconUrl.Contains(@"/feet/"))
				item.ItemCategory = ItemCategory.Feet;
			if (item.ItemIconUrl.Contains(@"/hands/"))
				item.ItemCategory = ItemCategory.Hands;
			if (item.ItemIconUrl.Contains(@"/back/"))
				item.ItemCategory = ItemCategory.Back;
			if (item.ItemIconUrl.Contains(@"/legs/"))
				item.ItemCategory = ItemCategory.Legs;
		}

		private int ExtractArmourGain(HtmlNode lorebookEntry)
		{
			var armourtext = ExtractInnerTextFromDiv(lorebookEntry, "itemarmor");
			int value;
			int.TryParse(armourtext.Split( new [] { ' ', '&' } ).First(), out value);
			return value;
		}

	}

	public static class HtmlNodeExtensions
	{
		public static bool CssClassIs(this HtmlNode node, string classId)
		{
			return node.GetAttributeValue("class", "").Split(' ').Contains(classId);
		}
	}
}
